import {
  BoxGeometry, BoxBufferGeometry,
  BufferGeometry,
  CircleGeometry, CircleBufferGeometry,
  Color,
  ConeGeometry, ConeBufferGeometry,
  Curve,
  CylinderGeometry, CylinderBufferGeometry,
  DodecahedronGeometry, DodecahedronBufferGeometry,
  DoubleSide,
  ExtrudeGeometry, ExtrudeBufferGeometry,
  Float32BufferAttribute,
  FontLoader,
  Group,
  IcosahedronGeometry, IcosahedronBufferGeometry,
  LatheGeometry, LatheBufferGeometry,
  LineSegments,
  LineBasicMaterial,
  Mesh,
  MeshPhongMaterial, PointsMaterial, Points,
  OctahedronGeometry, OctahedronBufferGeometry,
  ParametricGeometry, ParametricBufferGeometry,
  PerspectiveCamera,
  PlaneGeometry, PlaneBufferGeometry,
  PointLight,
  RingGeometry, RingBufferGeometry,
  Scene,
  Shape,
  ShapeGeometry, ShapeBufferGeometry,
  SphereGeometry, SphereBufferGeometry,
  TetrahedronGeometry, TetrahedronBufferGeometry,
  TextGeometry, TextBufferGeometry,
  TorusGeometry, TorusBufferGeometry,
  TorusKnotGeometry, TorusKnotBufferGeometry,
  TubeGeometry, TubeBufferGeometry,
  Vector2,
  Vector3,
  WireframeGeometry, EdgesGeometry,
  WebGLRenderer
} from "three";

import {GUI} from 'three/examples/jsm/libs/dat.gui.module.js';
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js';
import {ParametricGeometries} from 'three/examples/jsm/geometries/ParametricGeometries.js';

import '../../../css/base.css'
import {baseUrl} from '../../../js/base.js'

const twoPi = Math.PI * 2;
let isShowGeometry = ''

class CustomSinCurve extends Curve {

  constructor(scale = 1) {

    super();

    this.scale = scale;

  }

  getPoint(t, optionalTarget = new Vector3()) {

    const tx = t * 3 - 1.5;
    const ty = Math.sin(2 * Math.PI * t);
    const tz = 0;

    return optionalTarget.set(tx, ty, tz).multiplyScalar(this.scale);

  }

}

function updateGroupGeometry(mesh, geometry) {

  if (geometry.isGeometry) {

    geometry = new BufferGeometry().fromGeometry(geometry);

    console.warn('THREE.GeometryBrowser: Converted Geometry to BufferGeometry.');
  }

  mesh.children[0].geometry.dispose();
  mesh.children[1].geometry.dispose();

  if (isShowGeometry === 'EdgesGeometry') {
    mesh.children[0].geometry = new EdgesGeometry(geometry);
  } else {
    mesh.children[0].geometry = new WireframeGeometry(geometry);
  }
  mesh.children[1].geometry = geometry;

  if (isShowGeometry === 'EdgesGeometry' || isShowGeometry === 'WireframeGeometry') {
    mesh.children[1].visible = false
  } else {
    mesh.children[1].visible = true
  }
  // these do not update nicely together if shared

  isShowGeometry = ''
}

// heart shape

const x = 0, y = 0;

const heartShape = new Shape();

heartShape.moveTo(x + 5, y + 5);
heartShape.bezierCurveTo(x + 5, y + 5, x + 4, y, x, y);
heartShape.bezierCurveTo(x - 6, y, x - 6, y + 7, x - 6, y + 7);
heartShape.bezierCurveTo(x - 6, y + 11, x - 3, y + 15.4, x + 5, y + 19);
heartShape.bezierCurveTo(x + 12, y + 15.4, x + 16, y + 11, x + 16, y + 7);
heartShape.bezierCurveTo(x + 16, y + 7, x + 16, y, x + 10, y);
heartShape.bezierCurveTo(x + 7, y, x + 5, y + 5, x + 5, y + 5);

const guis = {

  BoxBufferGeometry: function (mesh) {

    const data = {
      width: 15,
      height: 15,
      depth: 15,
      widthSegments: 1,
      heightSegments: 1,
      depthSegments: 1
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new BoxBufferGeometry(
          data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments
        )
      );

    }

    const folder = gui.addFolder('THREE.BoxBufferGeometry');

    folder.add(data, 'width', 1, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 30).onChange(generateGeometry);
    folder.add(data, 'depth', 1, 30).onChange(generateGeometry);
    folder.add(data, 'widthSegments', 1, 10).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 1, 10).step(1).onChange(generateGeometry);
    folder.add(data, 'depthSegments', 1, 10).step(1).onChange(generateGeometry);

    generateGeometry();
    this['BoxBufferGeometry'].generateGeometry = generateGeometry


  },

  BoxGeometry: function (mesh) {

    const data = {
      width: 15,
      height: 15,
      depth: 15,
      widthSegments: 1,
      heightSegments: 1,
      depthSegments: 1
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new BoxGeometry(
          data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments
        )
      );

    }

    const folder = gui.addFolder('THREE.BoxGeometry');

    folder.add(data, 'width', 1, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 30).onChange(generateGeometry);
    folder.add(data, 'depth', 1, 30).onChange(generateGeometry);
    folder.add(data, 'widthSegments', 1, 10).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 1, 10).step(1).onChange(generateGeometry);
    folder.add(data, 'depthSegments', 1, 10).step(1).onChange(generateGeometry);

    generateGeometry();
    this['BoxGeometry'].generateGeometry = generateGeometry


  },

  CylinderBufferGeometry: function (mesh) {

    const data = {
      radiusTop: 5,
      radiusBottom: 5,
      height: 10,
      radialSegments: 8,
      heightSegments: 1,
      openEnded: false,
      thetaStart: 0,
      thetaLength: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new CylinderBufferGeometry(
          data.radiusTop,
          data.radiusBottom,
          data.height,
          data.radialSegments,
          data.heightSegments,
          data.openEnded,
          data.thetaStart,
          data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.CylinderBufferGeometry');

    folder.add(data, 'radiusTop', 0, 30).onChange(generateGeometry);
    folder.add(data, 'radiusBottom', 0, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 50).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 3, 64).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 1, 64).step(1).onChange(generateGeometry);
    folder.add(data, 'openEnded').onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);


    generateGeometry();
    this['CylinderBufferGeometry'].generateGeometry = generateGeometry


  },

  CylinderGeometry: function (mesh) {

    const data = {
      radiusTop: 5,
      radiusBottom: 5,
      height: 10,
      radialSegments: 8,
      heightSegments: 1,
      openEnded: false,
      thetaStart: 0,
      thetaLength: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new CylinderGeometry(
          data.radiusTop,
          data.radiusBottom,
          data.height,
          data.radialSegments,
          data.heightSegments,
          data.openEnded,
          data.thetaStart,
          data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.CylinderGeometry');

    folder.add(data, 'radiusTop', 1, 30).onChange(generateGeometry);
    folder.add(data, 'radiusBottom', 1, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 50).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 3, 64).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 1, 64).step(1).onChange(generateGeometry);
    folder.add(data, 'openEnded').onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);


    generateGeometry();
    this['CylinderGeometry'].generateGeometry = generateGeometry


  },

  ConeBufferGeometry: function (mesh) {

    const data = {
      radius: 5,
      height: 10,
      radialSegments: 8,
      heightSegments: 1,
      openEnded: false,
      thetaStart: 0,
      thetaLength: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new ConeBufferGeometry(
          data.radius,
          data.height,
          data.radialSegments,
          data.heightSegments,
          data.openEnded,
          data.thetaStart,
          data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.ConeBufferGeometry');

    folder.add(data, 'radius', 0, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 50).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 3, 64).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 1, 64).step(1).onChange(generateGeometry);
    folder.add(data, 'openEnded').onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);


    generateGeometry();
    this['ConeBufferGeometry'].generateGeometry = generateGeometry


  },

  ConeGeometry: function (mesh) {

    const data = {
      radius: 5,
      height: 10,
      radialSegments: 8,
      heightSegments: 1,
      openEnded: false,
      thetaStart: 0,
      thetaLength: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new ConeGeometry(
          data.radius,
          data.height,
          data.radialSegments,
          data.heightSegments,
          data.openEnded,
          data.thetaStart,
          data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.ConeGeometry');

    folder.add(data, 'radius', 0, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 50).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 3, 64).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 1, 64).step(1).onChange(generateGeometry);
    folder.add(data, 'openEnded').onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);


    generateGeometry();
    this['ConeGeometry'].generateGeometry = generateGeometry


  },

  CircleBufferGeometry: function (mesh) {

    const data = {
      radius: 10,
      segments: 32,
      thetaStart: 0,
      thetaLength: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new CircleBufferGeometry(
          data.radius, data.segments, data.thetaStart, data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.CircleBufferGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'segments', 0, 128).step(1).onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['CircleBufferGeometry'].generateGeometry = generateGeometry


  },

  CircleGeometry: function (mesh) {

    const data = {
      radius: 10,
      segments: 32,
      thetaStart: 0,
      thetaLength: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new CircleGeometry(
          data.radius, data.segments, data.thetaStart, data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.CircleGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'segments', 0, 128).step(1).onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['CircleGeometry'].generateGeometry = generateGeometry


  },

  DodecahedronGeometry: function (mesh) {

    const data = {
      radius: 10,
      detail: 0
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new DodecahedronGeometry(
          data.radius, data.detail
        )
      );

    }

    const folder = gui.addFolder('THREE.DodecahedronGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'detail', 0, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['DodecahedronGeometry'].generateGeometry = generateGeometry


  },

  DodecahedronBufferGeometry: function (mesh) {

    const data = {
      radius: 10,
      detail: 0
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new DodecahedronBufferGeometry(
          data.radius, data.detail
        )
      );

    }

    const folder = gui.addFolder('THREE.DodecahedronBufferGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'detail', 0, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['DodecahedronBufferGeometry'].generateGeometry = generateGeometry


  },

  EdgesGeometry: function (mesh) {
    isShowGeometry = 'EdgesGeometry'
    const selects = []
    Object.keys(guis).forEach(value => selects.push(value))

    const data = {
      select: 'BoxBufferGeometry',
    };

    function generateGeometry() {
      const geometry = new BoxGeometry(data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments)
      updateGroupGeometry(mesh, geometry);
    }

    const folder = gui.addFolder('THREE.EdgesGeometry');

    folder.add(data, 'select', selects).onChange((value) => {
      console.log(value)
      isShowGeometry = 'EdgesGeometry'
      this[data.select].generateGeometry()
    });

    this[data.select].generateGeometry()
    this['EdgesGeometry'].generateGeometry = generateGeometry
  },

  IcosahedronGeometry: function (mesh) {

    const data = {
      radius: 10,
      detail: 0
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new IcosahedronGeometry(
          data.radius, data.detail
        )
      );

    }

    const folder = gui.addFolder('THREE.IcosahedronGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'detail', 0, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['IcosahedronGeometry'].generateGeometry = generateGeometry


  },

  IcosahedronBufferGeometry: function (mesh) {

    const data = {
      radius: 10,
      detail: 0
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new IcosahedronBufferGeometry(
          data.radius, data.detail
        )
      );

    }

    const folder = gui.addFolder('THREE.IcosahedronBufferGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'detail', 0, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['IcosahedronBufferGeometry'].generateGeometry = generateGeometry


  },

  LatheBufferGeometry: function (mesh) {

    const points = [];

    for (let i = 0; i < 10; i++) {

      points.push(new Vector2(Math.sin(i * 0.2) * 10 + 5, (i - 5) * 2));

    }

    const data = {
      segments: 12,
      phiStart: 0,
      phiLength: twoPi
    };

    function generateGeometry() {

      const geometry = new LatheBufferGeometry(
        points, data.segments, data.phiStart, data.phiLength
      );

      updateGroupGeometry(mesh, geometry);

    }

    const folder = gui.addFolder('THREE.LatheBufferGeometry');

    folder.add(data, 'segments', 1, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'phiStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'phiLength', 0, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['LatheBufferGeometry'].generateGeometry = generateGeometry


  },

  LatheGeometry: function (mesh) {

    const points = [];

    for (let i = 0; i < 10; i++) {

      points.push(new Vector2(Math.sin(i * 0.2) * 10 + 5, (i - 5) * 2));

    }

    const data = {
      segments: 12,
      phiStart: 0,
      phiLength: twoPi
    };

    function generateGeometry() {

      const geometry = new LatheGeometry(
        points, data.segments, data.phiStart, data.phiLength
      );

      updateGroupGeometry(mesh, geometry);

    }

    const folder = gui.addFolder('THREE.LatheGeometry');

    folder.add(data, 'segments', 1, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'phiStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'phiLength', 0, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['LatheGeometry'].generateGeometry = generateGeometry


  },

  OctahedronGeometry: function (mesh) {

    const data = {
      radius: 10,
      detail: 0
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new OctahedronGeometry(
          data.radius, data.detail
        )
      );

    }

    const folder = gui.addFolder('THREE.OctahedronGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'detail', 0, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['OctahedronGeometry'].generateGeometry = generateGeometry


  },

  OctahedronBufferGeometry: function (mesh) {

    const data = {
      radius: 10,
      detail: 0
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new OctahedronBufferGeometry(
          data.radius, data.detail
        )
      );

    }

    const folder = gui.addFolder('THREE.OctahedronBufferGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'detail', 0, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['OctahedronBufferGeometry'].generateGeometry = generateGeometry


  },

  PlaneBufferGeometry: function (mesh) {

    const data = {
      width: 10,
      height: 10,
      widthSegments: 1,
      heightSegments: 1
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new PlaneBufferGeometry(
          data.width, data.height, data.widthSegments, data.heightSegments
        )
      );

    }

    const folder = gui.addFolder('THREE.PlaneBufferGeometry');

    folder.add(data, 'width', 1, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 30).onChange(generateGeometry);
    folder.add(data, 'widthSegments', 1, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 1, 30).step(1).onChange(generateGeometry);

    generateGeometry();
    this['PlaneBufferGeometry'].generateGeometry = generateGeometry


  },

  PlaneGeometry: function (mesh) {

    const data = {
      width: 10,
      height: 10,
      widthSegments: 1,
      heightSegments: 1
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new PlaneGeometry(
          data.width, data.height, data.widthSegments, data.heightSegments
        )
      );

    }

    const folder = gui.addFolder('THREE.PlaneGeometry');

    folder.add(data, 'width', 1, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 30).onChange(generateGeometry);
    folder.add(data, 'widthSegments', 1, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 1, 30).step(1).onChange(generateGeometry);

    generateGeometry();
    this['PlaneGeometry'].generateGeometry = generateGeometry


  },

  RingBufferGeometry: function (mesh) {

    const data = {
      innerRadius: 5,
      outerRadius: 10,
      thetaSegments: 8,
      phiSegments: 8,
      thetaStart: 0,
      thetaLength: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new RingBufferGeometry(
          data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.RingBufferGeometry');

    folder.add(data, 'innerRadius', 1, 30).onChange(generateGeometry);
    folder.add(data, 'outerRadius', 1, 30).onChange(generateGeometry);
    folder.add(data, 'thetaSegments', 1, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'phiSegments', 1, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['RingBufferGeometry'].generateGeometry = generateGeometry


  },

  RingGeometry: function (mesh) {

    const data = {
      innerRadius: 5,
      outerRadius: 10,
      thetaSegments: 8,
      phiSegments: 8,
      thetaStart: 0,
      thetaLength: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new RingGeometry(
          data.innerRadius, data.outerRadius, data.thetaSegments, data.phiSegments, data.thetaStart, data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.RingGeometry');

    folder.add(data, 'innerRadius', 1, 30).onChange(generateGeometry);
    folder.add(data, 'outerRadius', 1, 30).onChange(generateGeometry);
    folder.add(data, 'thetaSegments', 1, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'phiSegments', 1, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['RingGeometry'].generateGeometry = generateGeometry


  },

  SphereBufferGeometry: function (mesh) {

    const data = {
      radius: 15,
      widthSegments: 8,
      heightSegments: 6,
      phiStart: 0,
      phiLength: twoPi,
      thetaStart: 0,
      thetaLength: Math.PI
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new SphereBufferGeometry(
          data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.SphereBufferGeometry');

    folder.add(data, 'radius', 1, 30).onChange(generateGeometry);
    folder.add(data, 'widthSegments', 3, 32).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 2, 32).step(1).onChange(generateGeometry);
    folder.add(data, 'phiStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'phiLength', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['SphereBufferGeometry'].generateGeometry = generateGeometry


  },

  SphereGeometry: function (mesh) {

    const data = {
      radius: 15,
      widthSegments: 8,
      heightSegments: 6,
      phiStart: 0,
      phiLength: twoPi,
      thetaStart: 0,
      thetaLength: Math.PI
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new SphereGeometry(
          data.radius, data.widthSegments, data.heightSegments, data.phiStart, data.phiLength, data.thetaStart, data.thetaLength
        )
      );

    }

    const folder = gui.addFolder('THREE.SphereGeometry');

    folder.add(data, 'radius', 1, 30).onChange(generateGeometry);
    folder.add(data, 'widthSegments', 3, 32).step(1).onChange(generateGeometry);
    folder.add(data, 'heightSegments', 2, 32).step(1).onChange(generateGeometry);
    folder.add(data, 'phiStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'phiLength', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaStart', 0, twoPi).onChange(generateGeometry);
    folder.add(data, 'thetaLength', 0, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['SphereGeometry'].generateGeometry = generateGeometry


  },

  TetrahedronGeometry: function (mesh) {

    const data = {
      radius: 10,
      detail: 0
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new TetrahedronGeometry(
          data.radius, data.detail
        )
      );

    }

    const folder = gui.addFolder('THREE.TetrahedronGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'detail', 0, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['TetrahedronGeometry'].generateGeometry = generateGeometry


  },

  TetrahedronBufferGeometry: function (mesh) {

    const data = {
      radius: 10,
      detail: 0
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new TetrahedronBufferGeometry(
          data.radius, data.detail
        )
      );

    }

    const folder = gui.addFolder('THREE.TetrahedronBufferGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'detail', 0, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['TetrahedronBufferGeometry'].generateGeometry = generateGeometry


  },

  TextGeometry: function (mesh) {

    const data = {
      text: "TextGeometry",
      size: 5,
      height: 2,
      curveSegments: 12,
      font: "helvetiker",
      weight: "regular",
      bevelEnabled: false,
      bevelThickness: 1,
      bevelSize: 0.5,
      bevelOffset: 0.0,
      bevelSegments: 3
    };

    const fonts = [
      "helvetiker",
      "optimer",
      "gentilis",
      "droid/droid_serif"
    ];

    const weights = [
      "regular", "bold"
    ];

    function generateGeometry() {

      const loader = new FontLoader();
      loader.load(`${baseUrl}static/examples/fonts/` + data.font + '_' + data.weight + '.typeface.json', function (font) {

        const geometry = new TextGeometry(data.text, {
          font: font,
          size: data.size,
          height: data.height,
          curveSegments: data.curveSegments,
          bevelEnabled: data.bevelEnabled,
          bevelThickness: data.bevelThickness,
          bevelSize: data.bevelSize,
          bevelOffset: data.bevelOffset,
          bevelSegments: data.bevelSegments
        });
        geometry.center();

        updateGroupGeometry(mesh, geometry);

      });

    }

    //Hide the wireframe
    mesh.children[0].visible = true;

    const folder = gui.addFolder('THREE.TextGeometry');

    folder.add(data, 'text').onChange(generateGeometry);
    folder.add(data, 'size', 1, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 20).onChange(generateGeometry);
    folder.add(data, 'curveSegments', 1, 20).step(1).onChange(generateGeometry);
    folder.add(data, 'font', fonts).onChange(generateGeometry);
    folder.add(data, 'weight', weights).onChange(generateGeometry);
    folder.add(data, 'bevelEnabled').onChange(generateGeometry);
    folder.add(data, 'bevelThickness', 0.1, 3).onChange(generateGeometry);
    folder.add(data, 'bevelSize', 0, 3).onChange(generateGeometry);
    folder.add(data, 'bevelOffset', -0.5, 1.5).onChange(generateGeometry);
    folder.add(data, 'bevelSegments', 0, 8).step(1).onChange(generateGeometry);

    generateGeometry();
    this['TextGeometry'].generateGeometry = generateGeometry


  },

  TextBufferGeometry: function (mesh) {

    const data = {
      text: "TextBufferGeometry",
      size: 5,
      height: 2,
      curveSegments: 12,
      font: "helvetiker",
      weight: "regular",
      bevelEnabled: false,
      bevelThickness: 1,
      bevelSize: 0.5,
      bevelOffset: 0.0,
      bevelSegments: 3
    };

    const fonts = [
      "helvetiker",
      "optimer",
      "gentilis",
      "droid/droid_serif"
    ];

    const weights = [
      "regular", "bold"
    ];

    function generateGeometry() {

      const loader = new FontLoader();
      loader.load('static/examples/fonts/' + data.font + '_' + data.weight + '.typeface.json', function (font) {

        const geometry = new TextBufferGeometry(data.text, {
          font: font,
          size: data.size,
          height: data.height,
          curveSegments: data.curveSegments,
          bevelEnabled: data.bevelEnabled,
          bevelThickness: data.bevelThickness,
          bevelSize: data.bevelSize,
          bevelOffset: data.bevelOffset,
          bevelSegments: data.bevelSegments
        });
        geometry.center();

        updateGroupGeometry(mesh, geometry);

      });

    }

    //Hide the wireframe
    mesh.children[0].visible = true;

    const folder = gui.addFolder('THREE.TextBufferGeometry');

    folder.add(data, 'text').onChange(generateGeometry);
    folder.add(data, 'size', 1, 30).onChange(generateGeometry);
    folder.add(data, 'height', 1, 20).onChange(generateGeometry);
    folder.add(data, 'curveSegments', 1, 20).step(1).onChange(generateGeometry);
    folder.add(data, 'font', fonts).onChange(generateGeometry);
    folder.add(data, 'weight', weights).onChange(generateGeometry);
    folder.add(data, 'bevelEnabled').onChange(generateGeometry);
    folder.add(data, 'bevelThickness', 0.1, 3).onChange(generateGeometry);
    folder.add(data, 'bevelSize', 0, 3).onChange(generateGeometry);
    folder.add(data, 'bevelOffset', -0.5, 1.5).onChange(generateGeometry);
    folder.add(data, 'bevelSegments', 0, 8).step(1).onChange(generateGeometry);

    generateGeometry();
    this['TextBufferGeometry'].generateGeometry = generateGeometry


  },

  TorusBufferGeometry: function (mesh) {

    const data = {
      radius: 10,
      tube: 3,
      radialSegments: 16,
      tubularSegments: 100,
      arc: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new TorusBufferGeometry(
          data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc
        )
      );

    }

    const folder = gui.addFolder('THREE.TorusBufferGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'tube', 0.1, 10).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 2, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'tubularSegments', 3, 200).step(1).onChange(generateGeometry);
    folder.add(data, 'arc', 0.1, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['TorusBufferGeometry'].generateGeometry = generateGeometry


  },

  TorusGeometry: function (mesh) {

    const data = {
      radius: 10,
      tube: 3,
      radialSegments: 16,
      tubularSegments: 100,
      arc: twoPi
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new TorusGeometry(
          data.radius, data.tube, data.radialSegments, data.tubularSegments, data.arc
        )
      );

    }

    const folder = gui.addFolder('THREE.TorusGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'tube', 0.1, 10).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 2, 30).step(1).onChange(generateGeometry);
    folder.add(data, 'tubularSegments', 3, 200).step(1).onChange(generateGeometry);
    folder.add(data, 'arc', 0.1, twoPi).onChange(generateGeometry);

    generateGeometry();
    this['TorusGeometry'].generateGeometry = generateGeometry


  },

  TorusKnotBufferGeometry: function (mesh) {

    const data = {
      radius: 10,
      tube: 3,
      tubularSegments: 64,
      radialSegments: 8,
      p: 2,
      q: 3
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new TorusKnotBufferGeometry(
          data.radius, data.tube, data.tubularSegments, data.radialSegments,
          data.p, data.q
        )
      );

    }

    const folder = gui.addFolder('THREE.TorusKnotBufferGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'tube', 0.1, 10).onChange(generateGeometry);
    folder.add(data, 'tubularSegments', 3, 300).step(1).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 3, 20).step(1).onChange(generateGeometry);
    folder.add(data, 'p', 1, 20).step(1).onChange(generateGeometry);
    folder.add(data, 'q', 1, 20).step(1).onChange(generateGeometry);

    generateGeometry();
    this['TorusKnotBufferGeometry'].generateGeometry = generateGeometry


  },

  TorusKnotGeometry: function (mesh) {

    const data = {
      radius: 10,
      tube: 3,
      tubularSegments: 64,
      radialSegments: 8,
      p: 2,
      q: 3
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new TorusKnotGeometry(
          data.radius, data.tube, data.tubularSegments, data.radialSegments,
          data.p, data.q
        )
      );

    }

    const folder = gui.addFolder('THREE.TorusKnotGeometry');

    folder.add(data, 'radius', 1, 20).onChange(generateGeometry);
    folder.add(data, 'tube', 0.1, 10).onChange(generateGeometry);
    folder.add(data, 'tubularSegments', 3, 300).step(1).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 3, 20).step(1).onChange(generateGeometry);
    folder.add(data, 'p', 1, 20).step(1).onChange(generateGeometry);
    folder.add(data, 'q', 1, 20).step(1).onChange(generateGeometry);

    generateGeometry();
    this['TorusKnotGeometry'].generateGeometry = generateGeometry


  },

  ParametricBufferGeometry: function (mesh) {

    const data = {
      slices: 25,
      stacks: 25
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new ParametricBufferGeometry(ParametricGeometries.klein, data.slices, data.stacks)
      );

    }

    const folder = gui.addFolder('THREE.ParametricBufferGeometry');

    folder.add(data, 'slices', 1, 100).step(1).onChange(generateGeometry);
    folder.add(data, 'stacks', 1, 100).step(1).onChange(generateGeometry);

    generateGeometry();
    this['ParametricBufferGeometry'].generateGeometry = generateGeometry

  },

  ParametricGeometry: function (mesh) {

    const data = {
      slices: 25,
      stacks: 25
    };

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new ParametricGeometry(ParametricGeometries.klein, data.slices, data.stacks)
      );

    }

    const folder = gui.addFolder('THREE.ParametricGeometry');

    folder.add(data, 'slices', 1, 100).step(1).onChange(generateGeometry);
    folder.add(data, 'stacks', 1, 100).step(1).onChange(generateGeometry);

    generateGeometry();
    this['ParametricGeometry'].generateGeometry = generateGeometry

  },

  TubeGeometry: function (mesh) {

    const data = {
      segments: 20,
      radius: 2,
      radialSegments: 8
    };

    const path = new CustomSinCurve(10);

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new TubeGeometry(path, data.segments, data.radius, data.radialSegments, false)
      );

    }

    const folder = gui.addFolder('THREE.TubeGeometry');

    folder.add(data, 'segments', 1, 100).step(1).onChange(generateGeometry);
    folder.add(data, 'radius', 1, 10).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 1, 20).step(1).onChange(generateGeometry);

    generateGeometry();
    this['TubeGeometry'].generateGeometry = generateGeometry

  },

  TubeBufferGeometry: function (mesh) {

    const data = {
      segments: 20,
      radius: 2,
      radialSegments: 8
    };

    const path = new CustomSinCurve(10);

    function generateGeometry() {

      updateGroupGeometry(mesh,
        new TubeBufferGeometry(path, data.segments, data.radius, data.radialSegments, false)
      );

    }

    const folder = gui.addFolder('THREE.TubeBufferGeometry');

    folder.add(data, 'segments', 1, 100).step(1).onChange(generateGeometry);
    folder.add(data, 'radius', 1, 10).onChange(generateGeometry);
    folder.add(data, 'radialSegments', 1, 20).step(1).onChange(generateGeometry);

    generateGeometry();
    this['TubeBufferGeometry'].generateGeometry = generateGeometry

  },

  ShapeGeometry: function (mesh) {

    const data = {
      segments: 12
    };

    function generateGeometry() {

      const geometry = new ShapeGeometry(heartShape, data.segments);
      geometry.center();

      updateGroupGeometry(mesh, geometry);

    }

    const folder = gui.addFolder('THREE.ShapeGeometry');
    folder.add(data, 'segments', 1, 100).step(1).onChange(generateGeometry);

    generateGeometry();
    this['ShapeGeometry'].generateGeometry = generateGeometry

  },

  ShapeBufferGeometry: function (mesh) {

    const data = {
      segments: 12
    };

    function generateGeometry() {

      const geometry = new ShapeBufferGeometry(heartShape, data.segments);
      geometry.center();

      updateGroupGeometry(mesh, geometry);

    }

    const folder = gui.addFolder('THREE.ShapeBufferGeometry');
    folder.add(data, 'segments', 1, 100).step(1).onChange(generateGeometry);

    generateGeometry();
    this['ShapeBufferGeometry'].generateGeometry = generateGeometry

  },

  ExtrudeGeometry: function (mesh) {

    const data = {
      steps: 2,
      depth: 16,
      bevelEnabled: true,
      bevelThickness: 1,
      bevelSize: 1,
      bevelOffset: 0,
      bevelSegments: 1
    };

    const length = 12, width = 8;

    const shape = new Shape();
    shape.moveTo(0, 0);
    shape.lineTo(0, width);
    shape.lineTo(length, width);
    shape.lineTo(length, 0);
    shape.lineTo(0, 0);

    function generateGeometry() {

      const geometry = new ExtrudeGeometry(shape, data);
      geometry.center();

      updateGroupGeometry(mesh, geometry);

    }

    const folder = gui.addFolder('THREE.ExtrudeGeometry');

    folder.add(data, 'steps', 1, 10).step(1).onChange(generateGeometry);
    folder.add(data, 'depth', 1, 20).onChange(generateGeometry);
    folder.add(data, 'bevelThickness', 1, 5).step(1).onChange(generateGeometry);
    folder.add(data, 'bevelSize', 0, 5).step(1).onChange(generateGeometry);
    folder.add(data, 'bevelOffset', -4, 5).step(1).onChange(generateGeometry);
    folder.add(data, 'bevelSegments', 1, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['ExtrudeGeometry'].generateGeometry = generateGeometry

  },

  ExtrudeBufferGeometry: function (mesh) {

    const data = {
      steps: 2,
      depth: 16,
      bevelEnabled: true,
      bevelThickness: 1,
      bevelSize: 1,
      bevelOffset: 0,
      bevelSegments: 1
    };

    const length = 12, width = 8;

    const shape = new Shape();
    shape.moveTo(0, 0);
    shape.lineTo(0, width);
    shape.lineTo(length, width);
    shape.lineTo(length, 0);
    shape.lineTo(0, 0);

    function generateGeometry() {

      const geometry = new ExtrudeBufferGeometry(shape, data);
      geometry.center();

      updateGroupGeometry(mesh, geometry);

    }

    const folder = gui.addFolder('THREE.ExtrudeBufferGeometry');

    folder.add(data, 'steps', 1, 10).step(1).onChange(generateGeometry);
    folder.add(data, 'depth', 1, 20).onChange(generateGeometry);
    folder.add(data, 'bevelThickness', 1, 5).step(1).onChange(generateGeometry);
    folder.add(data, 'bevelSize', 0, 5).step(1).onChange(generateGeometry);
    folder.add(data, 'bevelOffset', -4, 5).step(1).onChange(generateGeometry);
    folder.add(data, 'bevelSegments', 1, 5).step(1).onChange(generateGeometry);

    generateGeometry();
    this['ExtrudeBufferGeometry'].generateGeometry = generateGeometry

  },

  WireframeGeometry: function (mesh) {
    isShowGeometry = 'WireframeGeometry'
    const selects = []
    Object.keys(guis).forEach(value => selects.push(value))

    const data = {
      select: 'BoxBufferGeometry',
    };

    function generateGeometry() {
      const geometry = new BoxGeometry(data.width, data.height, data.depth, data.widthSegments, data.heightSegments, data.depthSegments)
      updateGroupGeometry(mesh, geometry);
    }

    const folder = gui.addFolder('THREE.WireframeGeometry');

    folder.add(data, 'select', selects).onChange((value) => {
      console.log(value)
      isShowGeometry = 'WireframeGeometry'
      this[data.select].generateGeometry()
    });

    this[data.select].generateGeometry()
    this['WireframeGeometry'].generateGeometry = generateGeometry
  }

};

function chooseFromHash(mesh) {
  let selectedGeometry = "BoxGeometry";
  Object.keys(guis).forEach((value) => {
    if (guis[value] !== undefined) {
      if (selectedGeometry !== value) guis[value](mesh);
    }
  })
  setTimeout(() => guis[selectedGeometry](mesh))
}

const gui = new GUI();

const scene = new Scene();
scene.background = new Color(0x444444);

const camera = new PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 50);
camera.position.z = 30;

const canvas = document.getElementById('canvas');
const renderer = new WebGLRenderer({canvas, antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);

const orbit = new OrbitControls(camera, canvas);
orbit.enableZoom = false;

const lights = [];
lights[0] = new PointLight(0xffffff, 1, 0);
lights[1] = new PointLight(0xffffff, 1, 0);
lights[2] = new PointLight(0xffffff, 1, 0);

lights[0].position.set(0, 200, 0);
lights[1].position.set(100, 200, 100);
lights[2].position.set(-100, -200, -100);

scene.add(lights[0]);
scene.add(lights[1]);
scene.add(lights[2]);

const group = new Group();

let geometry = new BufferGeometry();
geometry.setAttribute('position', new Float32BufferAttribute([], 3));

const lineMaterial = new LineBasicMaterial({color: 0xffffff, transparent: true, opacity: 0.8});
const meshMaterial = new MeshPhongMaterial({color: 0x156289, emissive: 0x072534, side: DoubleSide, flatShading: true});
group.add(new LineSegments(geometry, lineMaterial));
group.add(new Mesh(geometry, meshMaterial));

chooseFromHash(group);

scene.add(group);

function render() {
  requestAnimationFrame(render);
  group.rotation.x += 0.005;
  group.rotation.y += 0.005;
  renderer.render(scene, camera);
}

window.addEventListener('resize', function () {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);

}, false);

render();
